﻿namespace Guia.Elements
{
    using System;
    using System.Diagnostics.Contracts;
    using System.Windows.Automation;
    using System.Windows.Forms;
    using Core;
    using Locator;
    using PatternServants;
    
    /// <summary>
    /// This class represents a particular cell within a DataGrid of the WPF Toolkit.
    /// Attention: cells cant be looked up. They can only be accessed by the DataGrid directly. 
    /// </summary>
    public class DataGridCell_WpfToolkit : AbstractDataGridCell, ISelectionItemMulti, IGridItem
    {
        /// <summary>
        /// Represents a helper-servant for the GridItem-pattern.
        /// </summary>
        private readonly IGridItem gridItemServant;

        /// <summary>
        /// Represents a helper-servant for the SelectionItem-pattern.
        /// </summary>
        private readonly ISelectionItemMulti selectionItemServant;

        /// <summary>
        /// Initializes a new instance of the DataGridCell_WpfToolkit class for the WPF Toolkit. 
        /// </summary>
        /// <param name="automationElement">The AutomationElement representating the DataGridCell.</param>
        public DataGridCell_WpfToolkit(AutomationElement automationElement)
            : base(automationElement)
        {
            this.selectionItemServant = new SelectionItemImpl(this);
            this.gridItemServant = new GridItemImpl(this);
        }

        /// <summary>
        /// Gets the default-condition to find a control by its type.
        /// </summary>
        public static SearchCondition ControlTypeCondition
        {
            get { return SearchCondition.ByControlType(ControlType.Custom).And(SearchCondition.ByClassName("DataGridCell")); }
        }

        /// <summary>
        /// Gets the number of the cell's column.
        /// </summary>
        public int Column
        {
            get
            {
                return this.gridItemServant.Column;
            }
        }

        /// <summary>
        /// Gets the number of the cell's row.
        /// </summary>
        public int Row
        {
            get
            {
                return this.gridItemServant.Row;
            }
        }

        /// <summary>
        /// Gets the cell's column-span.
        /// </summary>
        public int ColumnSpan
        {
            get
            {
                return this.gridItemServant.ColumnSpan;
            }
        }

        /// <summary>
        /// Gets the cell's row-span.
        /// </summary>
        public int RowSpan
        {
            get
            {
                return this.gridItemServant.RowSpan;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this DataGridCell is selected or not.
        /// If multiple selection is allowed, this selection causes that only this cell may be selected. Thus, other cells may be unselected. Expect this cell doesnt change its value throught the assignment.
        /// </summary>
        /// <exception cref="InvalidOperationException">This exception is thrown when the cell can't be selected, since it is not enabled or row-selection is enabled.</exception>
        public bool IsSelected
        {
            get
            {
                return this.selectionItemServant.IsSelected;
            }

            set
            {
                this.selectionItemServant.IsSelected = value;
            }
        }

        /// <summary>
        /// Gets or sets the textual value of this cell.
        /// </summary>
        /// <exception cref="ArgumentNullException">This exception is thrown if value is null</exception>
        public new string TextValue
        {
            get
            {
                return base.TextValue;
            }

            set
            {
                Contract.Requires<ArgumentNullException>(value != null);
                try
                {
                    // Used to provoke an exception if not supported
                    this.GetNestedElement<Label>();
                    this.DoubleClick();
                    this.GetNestedElement<TextBox>().Value = value;
                } 
                catch (ElementNotAvailableException)
                {
                    throw new OperationNotSupportedException(
                        "Could not set a text for the cell, since no nested Label / TextBox is available in this DataGridCell.");
                }
                finally
                {
                    SendKeys.SendWait("\r");
                }
            }
        }

        /// <summary>
        /// Gets a nested UiElement inside the cell of the given type. If there are more than one such element, the first element is returned.
        /// </summary>
        /// <typeparam name="T">The type of the desired UiElement.</typeparam>
        /// <returns>The first nested UiElement of the given type.</returns>
        /// <exception cref="ElementNotAvailableException">This exception is thrown, when no such element of the given type exists inside the cell.</exception>
        public T GetNestedElement<T>() where T : UiElement
        {
            T[] nestedElements = this.GetAll<T>();
            if (nestedElements.Length == 0)
            {
                throw new ElementNotAvailableException("There is no nested element of type \"" + typeof(T) + "\" inside the DataGrid-cell.");
            }

            return nestedElements[0];
        }

        /// <summary>
        /// Selects the Cell.
        /// If multiple selection is allowed, this selection causes that only this cell is selected. Thus, other cells may be unselected.
        /// </summary>
        /// <exception cref="InvalidOperationException">This exception is thrown when the cell can't be selected, since it is not enabled or row-selection is enabled.</exception>
        public void Select()
        {
            this.selectionItemServant.Select();
        }

        /// <summary>
        /// Unselects the cell.
        /// </summary>
        /// <exception cref="InvalidOperationException">This exception is thrown when the cell can't be selected/unselected, since it is not enabled or row-selection is enabled.</exception>
        public void Unselect()
        {
            this.selectionItemServant.Unselect();
        }

        /// <summary>
        /// Adds this Cell to the selection of its DataGrid.
        /// </summary>
        /// <exception cref="InvalidOperationException">An InvalidOperationException is thrown, when the DataGrid does not support multiple selection or is not able to support selection in general (e.g. when row-selection mode is enabled).</exception>
        public void AddToSelection()
        {
            this.selectionItemServant.AddToSelection();
        }

        /// <summary>
        /// Gets a textual value of this cell.
        /// </summary>
        /// <returns>String representation of this cell</returns>
        /// <exception cref="OperationNotSupportedException">This exception is thrown, when the textual value is not existent. This usually means, that there is no nested Label in this cell.</exception>
        protected override string GetTextValue()
        {
            try
            {
                return GetNestedElement<Label>().TextValue;
            } 
            catch (ElementNotAvailableException)
            {
                throw new OperationNotSupportedException("Could not get the textual value of this cell, since there exists no nested Label.");
            }
        }
    }
}
